package com.sulent.frame.file.utils;

import cn.hutool.core.lang.Assert;
import com.hierynomus.msdtyp.AccessMask;
import com.hierynomus.msfscc.FileAttributes;
import com.hierynomus.msfscc.fileinformation.FileIdBothDirectoryInformation;
import com.hierynomus.mssmb2.SMB2CreateDisposition;
import com.hierynomus.mssmb2.SMB2CreateOptions;
import com.hierynomus.mssmb2.SMB2ShareAccess;
import com.hierynomus.mssmb2.SMBApiException;
import com.hierynomus.smbj.SMBClient;
import com.hierynomus.smbj.auth.AuthenticationContext;
import com.hierynomus.smbj.connection.Connection;
import com.hierynomus.smbj.session.Session;
import com.hierynomus.smbj.share.DiskShare;
import com.hierynomus.smbj.share.File;
import com.sulent.frame.file.dto.DirTree;
import com.sulent.frame.file.dto.FileInfo;
import com.sulent.frame.file.impl.NasFileServer;
import lombok.Cleanup;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @ClassName NasUtils
 * @Description: TODO
 * @Author Submerge.
 * @Since 2024/11/7 17:26
 * @Version 1.0
 */
@Slf4j
public class NasSmbUtils {


    public static final String COMMON_USER = "root";
    public static final String COMMON_PROTOCOL = "smb";
    public static final Integer COMMON_TIMEOUT = 5 * 60 * 1000;

    SMBClient client;
    DiskShare share;
    String directory;


    public NasSmbUtils (NasFileServer nasFileServer){
        initClient(nasFileServer);
    }

    protected void initClient(NasFileServer nasFileServer) {
//        NasConnectionParam nasConnectionParam = (NasConnectionParam) baseConnectionParam;
        String host = nasFileServer.getHost();
        String user = nasFileServer.getUsername();
        String password = nasFileServer.getPassword();
        String shareName = nasFileServer.getShareName();
//        directory = nasConnectionParam.getDirectory();

        boolean folderExists = false;

        try {
            log.info("连接nas-smb协议,参数:host={}, shareName={},user={},password={}", host, shareName, user, password);

            client = new SMBClient();
            Connection connection = client.connect(host);
            AuthenticationContext ac = new AuthenticationContext(user, password.toCharArray(), "");
            Session session = connection.authenticate(ac);
            share = (DiskShare) session.connectShare(nasFileServer.getShareName());
            //folderExists = share.folderExists(directory);

        } catch (Exception e) {
            log.error("连接nas-smb协议异常,参数:host={},shareName={},user={},password={}", host, shareName, user, password, e);
            throw new RuntimeException("连接nas-smb协议异常:" + e.getMessage(), e);
        }
        //Assert.isTrue(folderExists, String.format("文件目录[%s]不存在", directory));
    }

    public void checkClient() {
        try {
            this.share.folderExists(directory);
        } catch (Exception e) {
            log.error("nas connect failed", e);
            throw new RuntimeException("nas connect failed", e);
        }
    }



    public void close() {
        client.close();
    }





    public List<FileInfo> getFileInfoByPath(String dir, boolean includeDir) {
        log.info("【nas获取目录信息】 开始获取, 目录路径: {}", dir);
        List<FileInfo> list = new ArrayList<>();
        for (FileIdBothDirectoryInformation file : share.list(dir)) {
//            log.info("【nas获取目录信息】 开始获取, 文件信息: {}", JSON.toJSON(file));
            if (file.getFileName().equals(".") || file.getFileName().equals("..")) {
                continue;
            }
            if (!StringUtils.endsWith(dir, "/")) {
                dir = dir + "/";
            }
            FileInfo fileInfo = new FileInfo();
            fileInfo.setFileName(file.getFileName());
            String path = dir + file.getFileName();
            fileInfo.setFilePath(path);
            if (share.folderExists(path)) {
                if (!includeDir) continue;
                fileInfo.setIsDirectory(true);
                fileInfo.setExtension("-");
            } else {
                fileInfo.setIsDirectory(false);
                fileInfo.setExtension(fileInfo.getFileName().substring(fileInfo.getFileName().lastIndexOf("/") + 1));
            }
            if (Objects.nonNull(file.getChangeTime().toDate())) {
                fileInfo.setModifyTime(LocalDateTime.ofInstant(file.getChangeTime().toDate().toInstant(), ZoneId.systemDefault()));
            }
            list.add(fileInfo);
        }
        return list;
    }


    
    public void download(String path, String fileName, HttpServletResponse response){

        log.info("【nas下载文件】 开始下载, 下载文件 : {}", path);
        File smbFileRead = share.openFile(path, EnumSet.of(AccessMask.GENERIC_READ), null, SMB2ShareAccess.ALL, SMB2CreateDisposition.FILE_OPEN, null);
        InputStream is  = smbFileRead.getInputStream();
        response.reset();
        try {
            response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8.name()));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        response.setContentType("application/octet-stream;charset=UTF-8");
        try {
            IOUtils.copy(is, response.getOutputStream());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }



   
    public List<String> getTableNames(String tableNameLike, Integer isIncludeFile) {
        List<String> directoryList = new ArrayList<>();
        getFolder(directoryList, directory, isIncludeFile);
        return directoryList;
    }


    private void getFolder(List<String> directoryList, String dir, Integer isIncludeFile) {
        for (FileIdBothDirectoryInformation file : share.list(dir)) {

            if (file.getFileName().equals(".") || file.getFileName().equals("..")) {
                continue;
            }
            if (!StringUtils.endsWith(dir, "/")) {
                dir = dir + "/";
            }
            String path = dir + file.getFileName();
            if (share.folderExists(path)) {
                //文件夹以"/"结尾
                directoryList.add(path + "/");
                getFolder(directoryList, path, isIncludeFile);
            } else if (share.fileExists(path) && isIncludeFile != 0) {
                directoryList.add(path);
            }
        }

    }

   


   
    public List<DirTree> getDirTreeByPath(String path,Integer level) {
        try {
            log.info("【nas获取目录树信息】 开始获取, 目录路径: {}", path);
            List<DirTree> dirTrees = new ArrayList<>();
            if (level==0) return dirTrees;
            for (FileIdBothDirectoryInformation file : share.list(path)) {
                //log.info("【nas获取目录信息】 开始获取, 文件信息: {}", JSON.toJSON(file));
                if (file.getFileName().equals(".") || file.getFileName().equals("..")) {
                    continue;
                }
                if (!StringUtils.endsWith(path, "/")) {
                    path = path + "/";
                }
                if (share.folderExists(path+file.getFileName())) {
                    DirTree dirTree= new DirTree();
                    dirTree.setName(file.getFileName());
                    dirTree.setChildren(getDirTreeByPath(path + file.getFileName(),level-1));
                    dirTrees.add(dirTree);
                }
            }
            return dirTrees;
        } catch (SMBApiException e) {
            throw new RuntimeException("nas获取目录树异常："+ e.getMessage(), e);
        }
    }

   
    public boolean upload(String path, String fileName, InputStream inputStream) {
        OutputStream outputStream = null;
        try {
            path = path.equals("/")? path : path+"/";
            fileName = path +fileName ;

            File openFile = share.openFile(fileName,
                    EnumSet.of(AccessMask.FILE_ADD_FILE),
                    EnumSet.of(FileAttributes.FILE_ATTRIBUTE_NORMAL),
                    EnumSet.of(SMB2ShareAccess.FILE_SHARE_WRITE,SMB2ShareAccess.FILE_SHARE_READ),
                    SMB2CreateDisposition.FILE_CREATE,
                    EnumSet.of(SMB2CreateOptions.FILE_RANDOM_ACCESS));

            outputStream = openFile.getOutputStream();
            IOUtils.copy(inputStream, outputStream);
        } catch (IOException e) {
            throw new RuntimeException("nas文件上传异常："+ e.getMessage(), e);
        }finally {
            if (outputStream!=null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    log.error("nas文件流关闭异常：{}",e.getMessage(),e);
                }
            }
        }
        return true;
    }





}
